﻿using System;
using System.Collections.Generic;
using System.Linq;
using NEH_SA.Model;
using NEH_SA.Utilities;

namespace NEH_SA.TABU
{
    public static class Tabu
    {
        private const int MaxCounter = 3;

        public static Rozwiązanie WygenerujRozwiązanie(Rozwiązanie nehResult, Problem problem)
        {
            var rand = new Random();

            var bestPermutation = Tools.GenerujPoczątkowąPermutacje(problem.LiczbaZadań, rand);
            var bestCMax = bestPermutation.ObliczCMax(problem);

            //  var bestPermutation = nehResult.Permutacja;
            //   var bestCMax = nehResult.CMax;

            int maxBadIter3X = 10 * problem.LiczbaMaszyn;
            int maxNeighboursVisited3X = 6 * problem.LiczbaZadań;
            const int maxTabuList3X = 60;

            int maxBadIter2X = 20 * problem.LiczbaMaszyn;
            int maxNeighboursVisited2X = 4 * problem.LiczbaZadań;
            const int maxTabuList2X = 20;

            int maxBadIterIns = 30 * problem.LiczbaMaszyn;
            int maxNeighboursVisitedIns = 2 * problem.LiczbaZadań;
            const int maxTabuListIns = 7;

            var tabuList3X = new List<Tuple<int, int>>();
            var tabuList2X = new List<Tuple<int, int>>();
            var tabuListIns = new List<Tuple<int, int>>();

            var currentPermutation = bestPermutation.ToList();

            for (int i = 0; i < MaxCounter; i++)
            {
                currentPermutation = SearchAlgorithm(GeneratePermutation3X, Delta3X, currentPermutation, problem,
                                                     maxBadIter3X, maxNeighboursVisited3X, maxTabuList3X, rand, ref tabuList3X, ref bestCMax,
                                                     ref bestPermutation);
                currentPermutation = SearchAlgorithm(GeneratePermutation2X, Delta2X, currentPermutation, problem,
                                                     maxBadIter2X, maxNeighboursVisited2X, maxTabuList2X, rand, ref tabuList2X, ref bestCMax,
                                                     ref bestPermutation);
                currentPermutation = SearchAlgorithm(GeneratePermutationIns, DeltaIns, currentPermutation, problem,
                                                     maxBadIterIns, maxNeighboursVisitedIns, maxTabuListIns, rand, ref tabuListIns, ref bestCMax,
                                                     ref bestPermutation);
            }
            return new Rozwiązanie(bestPermutation, bestCMax, problem.Nazwa, Algorytm.Tabu);
        }




        private static List<int> SearchAlgorithm(Func<List<int>, int, int, int, List<int>> generatePermutation, Func<List<int>, int, int, int, Problem, int> delta, List<int> currentPermutation, Problem problem, int maxBadIter, int maxNeighboursVisited, int maxTabuList, Random rand, ref List<Tuple<int, int>> tabuList, ref int bestCMax, ref List<int> bestPermutation)
        {
            var currentCmax = currentPermutation.ObliczCMax(problem);
            Tuple<int, int> forbiddenMove = null;
            for (int badIter = 0; badIter < maxBadIter; badIter++)
            {
                var bestNeighbourPermutation = new List<int>();
                var bestNeighbourCMax = int.MaxValue;

                for (int neighboursVisited = 0; neighboursVisited < maxNeighboursVisited; neighboursVisited++)
                {
                    var randomNumbers = Generate3DiffNumbers(0, currentPermutation.Count, rand);
                    List<int> neighbourPermutation = generatePermutation(currentPermutation, randomNumbers.Item1, randomNumbers.Item2, randomNumbers.Item3);
                    forbiddenMove = new Tuple<int, int>(currentPermutation.ElementAt(randomNumbers.Item1), randomNumbers.Item1);
                    if (!neighbourPermutation.IsOnTabu(tabuList) && delta(neighbourPermutation, randomNumbers.Item1, randomNumbers.Item2, randomNumbers.Item3, problem) < 0)
                    {
                        var neighbourCmax = neighbourPermutation.ObliczCMax(problem);
                        if (neighbourCmax < bestNeighbourCMax)
                        {
                            bestNeighbourCMax = neighbourCmax;
                            bestNeighbourPermutation = neighbourPermutation.ToList();
                        }

                        if (neighbourCmax < currentCmax)
                        {
                            if (neighbourCmax < bestCMax)
                            {
                                bestCMax = neighbourCmax;
                                bestPermutation = neighbourPermutation.ToList();
                            }
                            badIter--;
                            break;
                        }
                    }
                }
                if (bestNeighbourPermutation.Any())
                {
                    if (tabuList.Count == maxTabuList)
                    {
                        tabuList.RemoveAt(0);
                    }
                    tabuList.Add(forbiddenMove);
                    currentPermutation = bestNeighbourPermutation.ToList();
                    currentCmax = bestNeighbourCMax;
                }
                else
                {
                    break;
                }
            }
            return currentPermutation;
        }

        private static int Delta3X(List<int> neighbourPermutation, int a, int b, int c, Problem problem)
        {
            return -1;
        }
        private static int Delta2X(List<int> neighbourPermutation, int a, int b, int c, Problem problem)
        {
            return -1;
        }
        private static int DeltaIns(List<int> neighbourPermutation, int a, int b, int c, Problem problem)
        {
            return -1;
        }

        private static List<int> GeneratePermutation3X(List<int> currentPermutation, int a, int b, int c)
        {
            var list = new List<int> { a, b, c };
            list.Sort();
            return currentPermutation.Swap(list[0], list[1], list[2]);
        }

        private static List<int> GeneratePermutationIns(List<int> currentPermutation, int a, int b, int arg4)
        {
            var list = new List<int> { a, b };
            list.Sort();
            return currentPermutation.Move(list[0], list[1]);
        }

        private static List<int> GeneratePermutation2X(List<int> currentPermutation, int a, int b, int arg4)
        {
            var list = new List<int> { a, b };
            list.Sort();
            return currentPermutation.Swap(list[0], list[1]);
        }

        public static bool IsOnTabu(this List<int> permutation, List<Tuple<int, int>> tabuList)
        {
            return tabuList.Any(tuple => permutation.ElementAt(tuple.Item2) == tuple.Item1);
        }

        private static Tuple<int, int, int> Generate3DiffNumbers(int i, int count, Random rand)
        {
            var a = rand.Next(i, count);
            int b;
            int c;
            do
            {
                b = rand.Next(0, count);
            } while (b == a);

            do
            {
                c = rand.Next(0, count);
            } while ((c == a) || (c == b));
            return new Tuple<int, int, int>(a, b, c);
        }
    }
}
